package com.alibaba.sdk.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.sdk.model.OperationResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/**
 * 服务访问细节日志
 *
 * @ writer Shawn(Shawn_0217@163.com)
 * @ date & time 2021/4/26 21:09
 * @ company alibaba
 */
@Slf4j
@Component
@Aspect
@Order(-1)
public class DetailLogAspect {

    // 调用getParameterNames(method)获取参数集合
    private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    /**
     * 入参和出参的打印
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("execution(* com.alibaba.sdk.service..*Service.*(..))")
    public Object logServiceInvoke(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        StringBuffer classMethodBuf = new StringBuffer();
        classMethodBuf.append(joinPoint.getSignature());

        StringBuffer startBuf = new StringBuffer("Start to call Service method: ");
        startBuf.append(classMethodBuf);
        startBuf.append("\nParameters: ");
        if (ArrayUtils.isNotEmpty(joinPoint.getArgs())) {
            String[] argumentNames = getArgName(joinPoint);
            int argSize = joinPoint.getArgs().length;
            for (int i = 0; i < argSize; i++) {
                Object arg = joinPoint.getArgs()[i];
                String argName = argumentNames[i];
                startBuf.append(i + 1).append("-").append(argName).append(":");
                appendArgumentsContent(startBuf, arg);
            }
        } else {
            startBuf.append(": no parameter");
        }
        log.info(startBuf.toString());

        Object result = joinPoint.proceed();

        StringBuffer endBuffer = new StringBuffer("Finished to call Service method: ");
        endBuffer.append(classMethodBuf);
        endBuffer.append("\nReturn: ");
        appendArgumentsContent(endBuffer, result);
        endBuffer.append("\nUsed time: ");
        endBuffer.append(System.currentTimeMillis() - startTime);
        endBuffer.append("ms");
        log.info(endBuffer.toString());

        return result;
    }

    /**
     * 获取参数名称
     *
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private String[] getArgName(ProceedingJoinPoint joinPoint) throws Exception {
        Method interfaceMethod = ((MethodSignature) joinPoint.getSignature()).getMethod();
        Method method = joinPoint.getTarget().getClass().getDeclaredMethod(joinPoint.getSignature().getName(), interfaceMethod.getParameterTypes());

        return parameterNameDiscoverer.getParameterNames(method);
    }

    /**
     * 将参数值添加到日志的StringBuffer中
     *
     * @param logBuffer       日志StringBuffer
     * @param argumentsObject 参数对象
     */
    private void appendArgumentsContent(StringBuffer logBuffer, Object argumentsObject) {
        if (argumentsObject == null) {
            logBuffer.append("null");
        } else {
            //无权访问或字符串或OperationResult类型时直接打印
            if (argumentsObject.getClass().isPrimitive() || argumentsObject instanceof String || argumentsObject instanceof OperationResult) {
                logBuffer.append(argumentsObject);
            }
            //数组时，遍历并递归
            else if (argumentsObject.getClass().isArray()) {
                Object[] args = (Object[]) argumentsObject;
                logBuffer.append("size:").append(args.length).append("\n");
                for (Object sub : args) {
                    appendArgumentsContent(logBuffer, sub);
                }
            }
            //列表时，遍历并递归
            else if (argumentsObject instanceof Collection) {
                Collection argumentsObjects = (Collection) argumentsObject;
                logBuffer.append("size:").append(argumentsObjects.size()).append("\n");
                logBuffer.append(argumentsObjects);
            }
            //键值对时，打印其中内容
            else if (argumentsObject instanceof Map) {
                Map<?, ?> map = (Map<?, ?>) argumentsObject;
                logBuffer.append("size:").append(map.size()).append("\n");
                logBuffer.append(map);
            }
            //HttpServletResponse，打印其中内容
            else if (argumentsObject instanceof HttpServletResponse) {
                logBuffer.append(argumentsObject.toString());
            }
            //MultipartFile 打印文件名称
            else if (argumentsObject instanceof MultipartFile){
                MultipartFile file = (MultipartFile) argumentsObject;
                Map map =new HashMap();
                map.put("file", file.getOriginalFilename());
                String contentJson = JSON.toJSONString(map);
                logBuffer.append(contentJson);
                //其他，转为json后打印
            } else {
                String contentJson = JSON.toJSONString(argumentsObject);
                logBuffer.append(contentJson);
            }
        }
    }

}
